#include "handleTask.h"

/**
 * Function pointer to work method
 */
static void (*work)(void*, void*);

/**
 * The memory shared
 */
static void* mapped_file;

/**
 * Named Pipe reference
 */
char* NAMED_PIPE = "fifo";

/**
 * Mask to manage the signal set
 */
static sigset_t block_set;

/**
 * Map the given file
 */
static void* loadFile(char* filename);

/**
 * Send a real time signal to shell
 */
static void notifyShell(sigval_t value, int signum);

/**
 * Stop the current process, and put it in waiting for SIGUSR1
 */
static void putSleepProcess();

/**
 * Debug function
 */
void handler(int sig);

void initTaskHandler(void* handle, char* mem_file){
	char * erreur;
	work = dlsym(handle, "work");
	if ((erreur = dlerror()) != NULL)  {
		fprintf(stderr, "%s", erreur);
		exit(1);
	}
	
	mapped_file = loadFile(mem_file);

	struct sigaction sigact;
    sigfillset(&block_set);
    sigdelset(&block_set, SIGUSR1);

    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = 0;
    sigaction(SIGUSR1, &sigact, NULL);

}

void listenTasks(void){
	Task t;
	int nbr_read;
	
	for(;;){
		nbr_read = read(0, &t, sizeof(Task));
		switch(nbr_read){
		case -1:
			perror("Cannot read task");
			break;
		default:
			handleTask(&t);
			break;
		}
	}
}

void redirectStdoutToMkfifo(void){
	int fd;
	if ((fd = open(NAMED_PIPE, O_WRONLY)) == -1) {
		perror("open() reader for writing");
		exit(EXIT_FAILURE);
	}
	dup2(fd, 1);
}

void handleTask(Task* t){
	if(strcmp(t->task, "free") == 0){
		if(t -> blocParamId != -1){
			sigval_t value;
			value.sival_int = t -> blocParamId; 
			notifyShell(value, 2);
		}
	}
	else if(strcmp(t->task, "work") == 0){
		void* input = t -> blocParamId == -1 ? NULL : mapped_file+(t->blocParamId*BLOC_LENGHT);
		void* output =  t -> blocReturnId == -1 ? NULL : mapped_file+(t -> blocReturnId*BLOC_LENGHT);
		(*work)(input, output);
		
		if(t -> blocParamId != -1){
			sigval_t value;
			value.sival_int = t -> blocParamId; 
			notifyShell(value, 1);
		}
		
		if(t -> blocReturnId != -1){
			sigval_t value;
			value.sival_int = t -> blocReturnId; 
			notifyShell(value, 1);
		}

		/* Prints work return value to oreader process only if work is supposed to return a value */
		/*if (output != NULL)
			write(1, output, BLOC_LENGHT);*/
	}
	else if(strcmp(t->task, "waitfor") == 0) {
		/* A process is waiting, it wakes it up*/
		if(kill(t->pid, SIGCONT) == -1){
			perror("Cannot send signal continue");
			exit(EXIT_FAILURE);
		}
	}
	else if(strcmp(t->task, "waitall") == 0) {
		/*notity the shell that this process are ready to be stopped*/
		if(kill(getppid(), SIGUSR1) == -1){
			perror("Cannot send signal");
			exit(EXIT_FAILURE);
		}
		putSleepProcess();
	}
}

static void notifyShell(sigval_t value, int signum){
	if (sigqueue(getppid(), SIGRTMIN+signum, value) == -1) {
		perror("Cannot notify the shell");
		exit(-1);
	}
}

static void putSleepProcess(){
	/*stop the process itself and bloc all signals except SIGUSR1*/
	sigsuspend(&block_set);
}

static void* loadFile(char* filename) {

	size_t size = size_of(filename);

	if(size == 0) return NULL;

	int fd = open(filename, O_RDWR);
	if(fd == -1){
		perror("cannot load memory file");
		exit(EXIT_FAILURE);
	}
	
	void* map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	
	if(map == (void*)-1){
		perror("load memory : cannot map file");
		exit(EXIT_FAILURE);
	}
	close(fd);
	return map;
}
